﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.IO;
using 卷书.Utils;
using static 卷书.Global;
using PdfiumViewer;
using System.Drawing;
using PdfSharp.Pdf.IO;
using PdfSharp.Pdf.Advanced;
using Microsoft.WindowsAPICodePack.Dialogs;

namespace 卷书.UserControls
{
    /// <summary>
    /// LibraryView.xaml 的交互逻辑
    /// </summary>
    public partial class PdfViewer : UserControl
    {


        public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
"Source", typeof(string), typeof(PdfViewer), new PropertyMetadata(""));

        public string Source
        {
            get { return (string)GetValue(SourceProperty); }
            set
            {
                SetValue(SourceProperty, value);
                Init();
            }
        }

        private LibraryFile _LibraryFile;
        public LibraryFile LibraryFile
        {
            get
            {
                return _LibraryFile;
            }
            set
            {
                _LibraryFile = value;
                tagPanel.TagList = value.tags;
                descriptionTextBox.Text = value.descriptions;
                tagPanel.Refresh();
            }
        }


        public string LibraryName;

        private PdfDocument pdfDoc;
        private bool stopRendering = false;

        private int _CurrentPage = 1;
        private int _MaxPage = 1;

        private int CurrentPage
        {
            get
            {
                return _CurrentPage;
            }
            set
            {
                if (value <= 1) value = 1;

                _CurrentPage = value;
                CurrentPageTextBlock.Text = value.ToString();
            }
        }

        private int MaxPage
        {
            get
            {
                return _MaxPage;
            }
            set
            {
                _MaxPage = value;
                MaxPageTextBlock.Text = value.ToString();
            }
        }

        public PdfViewer()
        {
            InitializeComponent();
            this.DataContext = this;

            tagPanel.TagChanged += (s, e) =>
            {
                ListChangedEventArgs lce = (ListChangedEventArgs)e;
                tagPanel.TagList = new List<string>();
                tagPanel.TagList.AddRange(lce.List);
                LibraryFile.tags = lce.List;
                //保存
                LibraryStream.SaveLibraryFile(LibraryName, LibraryFile);
            };

        }

        public void Dispose()
        {
            var imgs = PdfContainer.Children.OfType<System.Windows.Controls.Image>().ToList();
            for (int i = imgs.Count - 1; i >= 0; i--)
            {
                imgs[i].Source = null;
                imgs[i] = null;
                GC.Collect();
            }
            var sideimgs = SidePdfContainer.Children.OfType<System.Windows.Controls.Image>().ToList();
            for (int i = sideimgs.Count - 1; i >= 0; i--)
            {
                sideimgs[i].Source = null;
                sideimgs[i] = null;
                GC.Collect();
            }


            PdfContainer.Children.Clear();
            SidePdfContainer.Children.Clear();
            pdfDoc?.Dispose();
        }

        public async void Init()
        {
            stopRendering = true;
            await Task.Delay(100);
            //清空资源
            Dispose();

            this.Visibility = Visibility.Collapsed;

            if (!File.Exists(Source))
            {
                StatusMessage = new StringBuilder("文件不存在");
                return;
            }
            double length = new FileInfo(Source).Length / 1024;
            if (length > 25  * 1024 )
            {
                StatusMessage = new StringBuilder("文件过大！");
                return;
            }

            pdfDoc = PdfDocument.Load(Source);
            StatusMessage = new StringBuilder(Source);
            MaxPage = pdfDoc.PageCount;

            if (MaxPage > 30)
            {
                StatusMessage = new StringBuilder("页码过大！");
                pdfDoc.Dispose();
                return;
            }
            this.Visibility = Visibility.Visible;

            CurrentPage = 1;
            double rate = 29.7 / 21;
            double width = SystemParameters.PrimaryScreenWidth;
            double height = width * rate;
            PdfContainer.Width = width / 2;
            //SidePdfContainer.Width = MinSideMenuWidth;
            stopRendering = false;
            PdfContainer.Children.Clear();
            SidePdfContainer.Children.Clear();
            for (int i = 0; i < pdfDoc.PageCount; i++)
            {
                if (stopRendering) break;
                var img = pdfDoc.Render(i, (int)width, (int)height, 96, 96, false);
                System.Windows.Controls.Image image = new System.Windows.Controls.Image();
                image.Source = BitmapHelper.BitmapToBitmapImage((Bitmap)img);
                image.Margin = new Thickness(5);
                image.Stretch = Stretch.Uniform;
                PdfContainer.Children.Add(image);

                //缩略图
                var sideimg = pdfDoc.Render(i, 500,(int) (500 * rate), 96, 96, false);
                Border border = new Border();
                //border.Background = System.Windows.Media.Brushes.LightGray;
                border.BorderThickness = new Thickness(2);
                border.Cursor = Cursors.Hand;
                border.Tag = i;
                //border.Margin = new Thickness(5);
                border.MouseEnter += (s, ev) =>
                {
                    border.BorderBrush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(255, 0, 127, 212));
                };
                border.MouseLeave += (s, ev) =>
                {
                    border.BorderBrush = System.Windows.Media.Brushes.Transparent;
                };
                border.PreviewMouseLeftButtonDown += (s, e) =>
                {
                    Border border1 = (Border)s;
                    var imgs = PdfContainer.Children.OfType<System.Windows.Controls.Image>().ToList();
                    scrollViewer.ScrollToTop();
                    imgs[int.Parse(border1.Tag.ToString())].BringIntoView();
                };

                System.Windows.Controls.Image sideimage = new System.Windows.Controls.Image();
                sideimage.Source = BitmapHelper.BitmapToBitmapImage((Bitmap)sideimg);
                //sideimage.Margin = new Thickness(5);
                sideimage.Stretch = Stretch.Uniform;
                border.Child = sideimage;
                SidePdfContainer.Children.Add(border);

                await Task.Delay(1);
            }

            pdfDoc.Dispose();
        }

        private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
            {
                if (PdfContainer.Width > SystemParameters.PrimaryScreenWidth)
                {
                    PdfContainer.Width = SystemParameters.PrimaryScreenWidth;
                    return;
                }
                else if (PdfContainer.Width < 500)
                {
                    PdfContainer.Width = 500;
                    return;
                }
                if (e.Delta > 0 && PdfContainer.Width + 100 <= SystemParameters.PrimaryScreenWidth)
                {
                    PdfContainer.Width += 100;
                }
                else if (e.Delta < 0 && PdfContainer.Width - 100 >= 500)
                {
                    PdfContainer.Width -= 100;
                }
                e.Handled = true;
            }
        }

        private void ExportImage(object sender, MouseButtonEventArgs e)
        {
            if (!File.Exists(Source)) return;
            List<PdfSharp.Pdf.PdfDictionary> images = new List<PdfSharp.Pdf.PdfDictionary>();
            PdfSharp.Pdf.PdfDocument document = PdfReader.Open(Source);
            foreach (PdfSharp.Pdf.PdfPage page in document.Pages)
            {
                // Get resources dictionary
                PdfSharp.Pdf.PdfDictionary resources = page.Elements.GetDictionary("/Resources");
                if (resources != null)
                {
                    // Get external objects dictionary
                    PdfSharp.Pdf.PdfDictionary xObjects = resources.Elements.GetDictionary("/XObject");
                    if (xObjects != null)
                    {
                        ICollection<PdfSharp.Pdf.PdfItem> items = xObjects.Elements.Values;
                        // Iterate references to external objects
                        foreach (PdfSharp.Pdf.PdfItem item in items)
                        {
                            PdfReference reference = item as PdfReference;
                            if (reference != null)
                            {
                                PdfSharp.Pdf.PdfDictionary xObject = reference.Value as PdfSharp.Pdf.PdfDictionary;
                                // Is external object an image?
                                if (xObject != null && xObject.Elements != null && xObject.Elements.GetString("/Subtype") != null && xObject.Elements.GetString("/Subtype") == "/Image")
                                {
                                    //ExportImage(xObject, ref imageCount);
                                    //仅 Jpeg
                                    try
                                    {
                                        if (xObject.Elements.GetName("/Filter") != null && xObject.Elements.GetName("/Filter") == "/DCTDecode")
                                        {
                                            images.Add(xObject);
                                        }
                                    }
                                    catch (NullReferenceException ex)
                                    {
                                        continue;
                                    }




                                }
                            }
                        }
                    }
                }
            }

            if (images.Count > 0)
            {
                var dialog = new CommonOpenFileDialog();
                dialog.Title = "选择导出的文件夹";
                dialog.IsFolderPicker = true;
                dialog.InitialDirectory = AppDomain.CurrentDomain.BaseDirectory;
                if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
                {
                    string path = dialog.FileName;
                    int successNumber = 0;
                    for (int i = 0; i < images.Count; i++)
                    {
                        try
                        {
                            byte[] stream = images[i].Stream.Value;
                            FileStream fs = new FileStream(Path.Combine(path, String.Format("Image-{0}.jpeg", i)), FileMode.Create, FileAccess.Write);
                            BinaryWriter bw = new BinaryWriter(fs);
                            bw.Write(stream);
                            bw.Close();
                            successNumber++;
                        }
                        catch (Exception ex)
                        {
                            Logger.LogF(ex);
                            continue;
                        }

                    }
                    StatusMessage = new StringBuilder($"成功导出 {successNumber}/{images.Count} 张图片");
                }
            }
            else
            {
                StatusMessage = new StringBuilder("该 PDF 中不存在 JPG 格式的图片！");
            }




        }

        private static void ExportImage(PdfSharp.Pdf.PdfDictionary image, ref int count)
        {
            string filter = image.Elements.GetName("/Filter");
            switch (filter)
            {
                case "/DCTDecode":
                    ExportJpegImage(image, ref count);
                    break;
                case "/FlateDecode":
                    ExportAsPngImage(image, ref count);
                    break;
            }
        }

        private static void ExportJpegImage(PdfSharp.Pdf.PdfDictionary image, ref int count)
        {
            // Fortunately JPEG has native support in PDF and exporting an image is just writing the stream to a file.
            byte[] stream = image.Stream.Value;
            FileStream fs = new FileStream(String.Format("Image{0}.jpeg", count++), FileMode.Create, FileAccess.Write);
            BinaryWriter bw = new BinaryWriter(fs);
            bw.Write(stream);
            bw.Close();
        }
        static void ExportAsPngImage(PdfSharp.Pdf.PdfDictionary image, ref int count)
        {
            int width = image.Elements.GetInteger(PdfImage.Keys.Width);
            int height = image.Elements.GetInteger(PdfImage.Keys.Height);
            int bitsPerComponent = image.Elements.GetInteger(PdfImage.Keys.BitsPerComponent);
        }



        private void ExportText(object sender, MouseButtonEventArgs e)
        {
            if (!File.Exists(Source)) return;
            string text = PDFHelper.GetText(Source);

            System.Windows.Forms.SaveFileDialog dialog = new System.Windows.Forms.SaveFileDialog();
            dialog.FileName = Path.GetFileNameWithoutExtension(Source) + ".txt";
            dialog.Title = "保存文件";
            dialog.InitialDirectory = AppDomain.CurrentDomain.BaseDirectory;
            dialog.Filter = "txt|*.txt";
            dialog.FilterIndex = 1;
            dialog.RestoreDirectory = true;
            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                string file = dialog.FileName;
                using (StreamWriter sw = new StreamWriter(file, false))
                {
                    sw.Write(text);
                }
                StatusMessage = new StringBuilder("成功保存到 " + file);
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {

        }


        private bool canFlip = false;
        private void NextPage(object sender, RoutedEventArgs e)
        {
            canFlip = true;
            if (CurrentPage + 1 <= MaxPage)
                CurrentPage++;


        }

        private void PreviousPage(object sender, RoutedEventArgs e)
        {
            scrollViewer.ScrollToTop();
            canFlip = true;
            if (CurrentPage - 1 >= 1)
                CurrentPage--;


        }

        private void OpenDefault(object sender, MouseButtonEventArgs e)
        {
            FileHelper.TryOpenFile(Source);
        }

        private bool CanDragScroll = false;
        private System.Windows.Point firstPoint;
        private double offset = 0;
        private void SetDragScroll(object sender, MouseButtonEventArgs e)
        {
            CanDragScroll = true;
            firstPoint = Mouse.GetPosition(this);
            offset = scrollViewer.VerticalOffset;
        }

        private void DisableDragScroll(object sender, MouseButtonEventArgs e)
        {
            CanDragScroll = false;
        }

        private void DragScroll(object sender, MouseEventArgs e)
        {

            if (CanDragScroll)
            {
                System.Windows.Point point = Mouse.GetPosition(this);
                scrollViewer.ScrollToVerticalOffset(offset - point.Y + firstPoint.Y);
            }



        }

        private void PdfContainer_MouseLeave(object sender, MouseEventArgs e)
        {
            CanDragScroll = false;
        }

        private void CurrentPageTextBlock_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (PdfContainer == null || PdfContainer.Children.Count == 0) return;
            if (canFlip)
            {
                var imgs = PdfContainer.Children.OfType<System.Windows.Controls.Image>().ToList();
                imgs[CurrentPage - 1].BringIntoView();
                canFlip = false;
            }



        }


        private void scrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            if (PdfContainer == null || PdfContainer.Children.Count == 0 || SidePdfContainer.Children.Count==0) return;
            var imgs = PdfContainer.Children.OfType<System.Windows.Controls.Image>().ToList();
            double height = imgs[0].ActualHeight;

            double offset = scrollViewer.VerticalOffset;
            CurrentPage = (int)(offset / height) + 1;

            var sideimgs = SidePdfContainer.Children.OfType<Border>().ToList();
            if (sideimgs.Count == 0) return;

            sideimgs[CurrentPage - 1].BringIntoView();

            if (scrollViewer.VerticalOffset == scrollViewer.ScrollableHeight)
            {
                cb.IsChecked = false;
            }
            else if (scrollViewer.VerticalOffset == 0)
            {
                cb.IsChecked = true;
            }
        }

        private void Scroll(object sender, MouseButtonEventArgs e)
        {
            if ((bool)cb.IsChecked)
            {
                scrollViewer.ScrollToEnd();
            }
            else
            {
                scrollViewer.ScrollToTop();
            }
        }

        private void Shrink(object sender, MouseButtonEventArgs e)
        {
            if (PdfContainer.Width < 500)
            {
                PdfContainer.Width = 500;
                return;
            }
            else if (PdfContainer.Width - 100 >= 500)
            {
                PdfContainer.Width -= 100;
            }
        }

        private void Enlarge(object sender, MouseButtonEventArgs e)
        {
            if (PdfContainer.Width > SystemParameters.PrimaryScreenWidth)
            {
                PdfContainer.Width = SystemParameters.PrimaryScreenWidth;
                return;
            }
            if (PdfContainer.Width + 100 <= SystemParameters.PrimaryScreenWidth)
            {
                PdfContainer.Width += 100;
            }
        }

        private bool _IsSideMenuChecked = true;
        public bool IsSideMenuChecked
        {
            get
            {
                return _IsSideMenuChecked;
            }
            set
            {
                _IsSideMenuChecked = value;
                if (value) SideMenu.Visibility = Visibility.Collapsed;
                else SideMenu.Visibility = Visibility.Visible;
            }
        }
        private void SetSideMenuChecked(object sender, RoutedEventArgs e)
        {
            CheckBox checkBox = sender as CheckBox;
            var cbs = SideCheckBoxes.Children.OfType<CheckBox>().ToList();
            cbs.Where(arg => arg != checkBox).ToList().ForEach(arg => arg.IsChecked = false);
            if ((bool)checkBox.IsChecked)
            {
                IsSideMenuChecked = false;
            }
            else
            {
                IsSideMenuChecked = true;
            }

            ShowSideMenu(cbs.IndexOf(checkBox));

        }

        private void ShowSideMenu(int idx)
        {
            if (SideMenu != null)
            {
                SideMenu.SelectedIndex = idx;
            }


        }

        private bool IsDragingSideMenu = false;
        private double SideMenuWidth = 200;
        private double MinSideMenuWidth = 150;
        private double _MaxSideMenuWidth = 500;

        private double MaxSideMenuWidth
        {
            get
            {
                return _MaxSideMenuWidth;
            }
            set
            {
                _MaxSideMenuWidth = value;
                if (SideMenu.Width > value)
                {
                    SideMenu.Width = value;
                    SideMenuWidth = value;
                }
            }
        }

        private void DragRectangle_MouseMove(object sender, MouseEventArgs e)
        {
            if (SideMenuWidth >= MinSideMenuWidth)
            {
                if (sender is System.Windows.Shapes.Rectangle rectangle) rectangle.Cursor = Cursors.SizeWE;
            }
            if (IsDragingSideMenu)
            {
                this.Cursor = Cursors.SizeWE;
                double width = e.GetPosition(SideMenu).X;
                if (width < MinSideMenuWidth)
                {
                    SideMenuWidth = MinSideMenuWidth;
                }
                else if (width > MaxSideMenuWidth)
                {
                    SideMenuWidth = MaxSideMenuWidth;
                }
                else
                {
                    SideMenuWidth = width;
                }
                SideMenu.Width = SideMenuWidth;

            }
        }

        private void DragRectangle_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                IsDragingSideMenu = true;
            }
        }

        private void DragRectangle_MouseUp(object sender, MouseButtonEventArgs e)
        {
            IsDragingSideMenu = false;
            this.Cursor = Cursors.Arrow;
        }

        private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            Console.WriteLine(e.AddedItems[0]);
        }


        private async void ReloadFile(object sender, MouseButtonEventArgs e)
        {
            string source = this.Source;
            Dispose();
            this.Source = source;
        }

        private void SetTextBoxFocus(object sender, MouseButtonEventArgs e)
        {
            descriptionTextBox.Focus();
        }

        private void SaveDescriptions(object sender, RoutedEventArgs e)
        {
            //保存注释
            if (descriptionTextBox.Text == "") return;
            LibraryFile.descriptions = descriptionTextBox.Text;
            if (LibraryFile.tags == null) LibraryFile.tags = new List<string>();
            //保存
            LibraryStream.SaveLibraryFile(LibraryName, LibraryFile);
        }

        private void HidePlaceHolder(object sender, TextChangedEventArgs e)
        {
            TextBox textBox = sender as TextBox;
            if (textBox.Text == "")
            {
                hintTextBlock.Visibility = Visibility.Visible;
            }
            else
            {
                hintTextBlock.Visibility = Visibility.Collapsed;
            }

            }
    }
}
